/*
 * Copyright (C) 2022-2022. Huawei Technologies Co., Ltd. All rights reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.openlookeng.utils;

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@Slf4j
public class TreadUtils
{
    @SuppressWarnings("checkstyle:ExplicitInitialization")
    private static ThreadPoolExecutor threadPool = null;
    private static final String POOL_NAME = "myPool";
    // waiting queue length
    private static final int BLOCKING_QUEUE_LENGTH = 1000;
    // idle thread survival time
    private static final int KEEP_ALIVE_TIME = 60 * 1000;

    private void ThreadPoolUtils()
    {
    }

    /**
     * Execute directly without return value
     *
     * @param runnable tasks that need to be run
     */
    public static void execute(Runnable runnable)
    {
        getThreadPool().execute(runnable);
    }

    /**
     * Execute with return value
     * When using Future.get() to get the return value in the main thread, it will block the main thread until the task is executed.
     *
     * @param callable tasks that need to be run
     */
    public static <T> Future<T> submit(Callable<T> callable)
    {
        return getThreadPool().submit(callable);
    }

    private static synchronized ThreadPoolExecutor getThreadPool()
    {
        if (threadPool == null) {
            // Get the number of processors
            int cpuNum = Runtime.getRuntime().availableProcessors();
            // Calculate a reasonable number of concurrent threads based on the number of CPUs
            int maximumPoolSize = cpuNum * 2 + 1;
            threadPool = new ThreadPoolExecutor(cpuNum,
                    maximumPoolSize,
                    KEEP_ALIVE_TIME,
                    TimeUnit.MILLISECONDS,
                    new LinkedBlockingDeque<>(BLOCKING_QUEUE_LENGTH),
                    new ThreadFactoryBuilder().setNameFormat(POOL_NAME + "-%d").build(),
                    new ThreadPoolExecutor.AbortPolicy()
                    {
                        @Override
                        public void rejectedExecution(Runnable r, ThreadPoolExecutor e)
                        {
                            log.info("线程爆炸了，当前运行线程总数：{}，活动线程数：{}。等待队列已满，等待运行任务数：{}",
                                    e.getPoolSize(),
                                    e.getActiveCount(),
                                    e.getQueue().size());
                        }
                    });
        }
        return threadPool;
    }
}
